/*
 * Copyright (c) 2024 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import { ArkField, ArkFile, ArkMethod, LineColPosition } from "../ArkAnalyzer/src";
import { Stmt } from "../ArkAnalyzer/src/core/base/Stmt";
import { parseArkStmt } from "./nodeUtils/stmts";

export class ArkFieldDefStmt extends Stmt {
    private arkField: ArkField;

    constructor(arkField: ArkField) {
        super();
        this.arkField = arkField;
        this.setOriginPositionInfo(arkField.getOriginPosition());
        this.setText(arkField.getDeclaringArkClass().getSignature().getClassName() + '.' + arkField.getName());
    }

    public getArkField(): ArkField {
        return this.arkField;
    }

    toString(): string {
        return this.text === undefined ? "" : this.text;
    }
}

export class ArkMethodDefStmt extends Stmt {
    private method:ArkMethod;
    private returnStmts:Stmt[]=[];

    constructor(method: ArkMethod) {
        super();
        this.method = method;
        let lineCol: LineColPosition = new LineColPosition(this.getNumber(method.getLine()), this.getNumber(method.getColumn()));
        this.setOriginPositionInfo(lineCol);
        this.setText("method:"+method.getSignature().toString());
    }

    public getReturnStmts() {
        return this.returnStmts;
    }

    public addReturnStmt(returnStmt: Stmt) {
        this.returnStmts.push(returnStmt);
    }

    public getParameters() {
        return this.method.getParameters();
    }

    public getMethod() {
        return this.method;
    }

    toString(): string {
        return this.text === undefined ? "" : this.text;
    }

    private getNumber(num: number | null): number {
        if (num === null) {
            return -1;
        } else {
            return num;
        }
    }
}

export class Node {
    private static nodeNum: number = 0;
    private id: number;
    private file: string;
    private stmt: Stmt;
    private lineNo: number;
    private text: string;  // judge same node, maybe can be removed?
    private content: string;
    private methodSignature: string;
    
    constructor(stmt: Stmt, arkFile:ArkFile, methodSignature: string) {
        this.methodSignature = methodSignature;
        this.id = ++Node.nodeNum;
        this.stmt = stmt;
        this.text = stmt.toString();
        this.lineNo = stmt.getOriginPositionInfo().getLineNo() | 0;
        this.file = arkFile.getFileSignature().toString().substring(0,arkFile.getFileSignature().toString().length-1)
        const jsonString = JSON.stringify(parseArkStmt(stmt));
        this.content = jsonString;
    }

    public getText(): string {
        return this.text;
    }

    public getStmt(): Stmt {
        return this.stmt;
    }

    public getLineNo(): number {
        return this.lineNo;
    }

    public getFile(): string {
        return this.file;
    }

    public getOriginalStmt(): string {
        let originalStmt: string | undefined = this.stmt.getOriginalText();
        if (originalStmt === undefined) {
            return this.stmt.toString();
        }
        else {
            const regex = /(['"`])/g;
            return originalStmt.replace(regex, '\\$1');
        }
    }

    public getCypherBatch() {
        let originalStmt = this.getOriginalStmt().substring(0, 200);
        // const jsonContent = this.content.replace("\'", "\\\'");

        return `.addV('code')
                    .property('code','''${escapeSpecialCharacters(originalStmt)}''')
                    .property('node-id','${this.id}')
                    .property('lineno',${this.getLineNo()})
                    .property('json', '''${escapeSpecialCharacters(this.content)}''')
                    .property('file', '''${this.getFile()}''')
                    .property('IR', '''${escapeSpecialCharacters(this.stmt.toString())}''')
                    .id()
                    .as('${this.getId()}')`;
    }

    public getId() {
        return this.methodSignature + this.id;
    }
}

function escapeSpecialCharacters(input: string): string {
    return input.replace(/\\/g, '\\\\')
                .replace(/"/g, '\\"')
                .replace(/\$/g, '\\$');
}
